Repository navigation
docs: client integration guide, admission-control scope, and README client library - #21
Merged
Merged
Conversation
Adds a "Client Integration" section covering the decision a client makes on startup -- which endpoint to call given whether an agent key exists and whether the hApp is installed -- why reconnect goes first (both orders are safe, so it is about what each request needs from the caller: reconnect a key and a signature, join possibly claims and a user prompt), and the requirement to record the agent-key-to-hApp association locally before calling /v1/join. The Overview's flow summary gains the matching recovery stanza. It lands as section 4, so the sections after it are renumbered: Error Response Format 4->5, CORS and Rate Limiting 5->6, Security Considerations 6->7, Authentication Methods Reference 7->8, Example Flows 8->9 (and its 8.x subsections to 9.x), TypeScript Type Definitions 9->10. Two cross-references name a moved section and are updated: auth methods in the /v1/info field table, and the src/types.ts header comment pointing at the type definitions. References to 3.x subsections are unaffected. Also adds example flow 9.10, recovery after an interrupted install, and corrects flow 9.5's reconnect response, which named a `linker_urls_expire_at` field the service does not return -- expiry is per linker_urls entry.
Security Considerations now states what the service does and does not do: admission control, not fork prevention. A key holder can copy the conductor directory and fork without ever calling the service; that is Holochain's problem, handled by validation and warrants. Records why the 409 stays as it is -- /v1/join has no proof of key possession, so an idempotent join would turn a public agent key into a provisioning credential and skip auth evaluation. Adds the reconnect replay window: the signed payload is the timestamp alone within a configurable tolerance (default 300s) and ready sessions do not expire, so an observed request replayed inside the window returns the session token. Notes the available mitigations and that signing over the agent key plus a nonce is the durable fix. Also corrects the session-expiry bullet, which claimed 1 hour pending / 24 hours ready; the stores never expire ready sessions and pending sessions use session.pending_ttl_seconds (default 24 hours).
Covers JoiningClient: constructing one, choosing a network, driving the join/verify/provision flow, and the crash-recovery path via reconnect. Both construction paths get their own example rather than one worked example plus a prose aside, since which one applies is decided by whether the app domain publishes a well-known document, not by preference -- and the choice determines whether join() can route to a network on its own. The three forms of join()'s network argument are likewise shown as three calls instead of described as one three-way parameter. Provision handling branches on what is present instead of assuming a shape. Every field is optional and which ones arrive is a property of the deployment: a membrane-proof-only or gateway-only service returns no linker_urls at all, and /v1/info omits linker_info to match, so an absent linker is the normal case for those deployments rather than an error.
ThetaSinner
approved these changes
Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Documentation only — no behavior changes. Stacks on the multi-network PR (merge that first); it documents the session-recovery reconnect that PR adds.
There was no client-integration documentation anywhere:
JOINING_SERVICE_API.mddocuments each endpoint in isolation and its example flows are all happy paths from a clean slate, and the README never mentionedJoiningClient. An app developer embedding the client had nothing telling them what to call on startup.JOINING_SERVICE_API.md§4 "Client Integration" — the startup decision tree, decided entirely by local state: hApp installed → nothing to do; no agent key → generate and join; agent key present but hApp not installed → reconnect first, and only fall through to join on403 agent_not_joined. Reconnect goes first because it needs only the key and a signature, while join may need claims, an invite code, or an interactive prompt — so a crashed install recovers silently and auth is surfaced only when the agent genuinely has to join something new. Also states, normatively, that clients persist the agent-key→hApp association before calling/v1/join: local-write-before-remote-call is orderable, the reverse is not, and getting it wrong burns an invite code (fatal with single-use codes).409 agent_already_joinedis defended on its actual grounds (join requires no proof of key possession; the session token is a bearer credential for provision) rather than as a fork guard. Recorded so the "just make join idempotent" argument doesn't need re-litigating each time someone hits the 409.Two pre-existing inaccuracies fixed while in here: the security section claimed session expiry times that don't match the stores (ready sessions never expire; pending TTL default is 86400s), and one example flow showed a
linker_urls_expire_atfield that doesn't exist (real shape is per-entryexpires_at).Sections 4–9 renumber to 5–10 to make room for the new section; every cross-reference was audited (one moved reference updated, plus the
src/types.tsheader comment that names the types section — the only non-doc line in this PR).